﻿using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using Extensions;
using System.Reflection;
using System;


namespace TC
{
    public class FlowNodeSurface : NodeSurface<FlowNode>
    {
        public List<NodeComponentSurface> inPortSurface = new List<NodeComponentSurface>();
        public List<NodeComponentSurface> outPortSurface = new List<NodeComponentSurface>();
        private Color tmpColor;
        public Vector2 inRealBodySize;
        public Vector2 outRealBodySize;
        private FieldInfo contextKeyField;
        private string contextNodeName;
        public ValueWrap contextVar { get; private set; }

        #region Header相关

        public string showName
        {
            get
            {
                if (contextKeyField != null && contextVar != null) { return contextNodeName; }
                return target.GetType().Name;
            }
        }
        private GUIStyle _basicHeaderStyle;
        private GUIStyle basicHeaderStyle
        {
            get
            {
                if (_basicHeaderStyle == default)
                {
                    _basicHeaderStyle = new GUIStyle("button");
                    _basicHeaderStyle.fontSize = 16;
                    _basicHeaderStyle.normal.textColor = EditorGUIUtility.isProSkin?
                        new Color(0.0f / 255, 200.0f / 255, 200.0f / 255):
                        new Color(0.0f / 255, 125.0f / 255, 0.0f / 255);
                    _basicHeaderStyle.fontStyle = FontStyle.BoldAndItalic;
                    _basicHeaderStyle.margin = new RectOffset(0, 0, 0, 0);
                    _basicHeaderStyle.padding = new RectOffset(5, 5, 4, 4);
                }
                return _basicHeaderStyle;
            }
        }
        private GUIStyle headerStyle;
        private Vector2 realHeaderSize;        
        public Vector2 headerSize { get => new Vector2(size.x, realHeaderSize.y); }// 显示用的 header size
        #endregion

        #region 高亮显示
        public bool isActive { get; set; } = false;
        [NonSerialized]
        private Color hightLightColor = EditorGUIUtility.isProSkin ?
            new Color(255.0f / 255, 100.0f / 255, 200.0f / 255):
            new Color(255.0f / 255, 153.0f / 255, 255.0f / 255);
        [NonSerialized]
        private GUIStyle highlightStyle = new GUIStyle("button");
        [NonSerialized]
        private Rect basicHighLightRelativeRect = new Rect(-4, -4, 8, 8);
        [NonSerialized]
        public Rect hightLightRelativeRect;
        #endregion

        #region 整体相关
        private Vector2 minSize { get; set; } = new Vector2(80, 30);
        private GUIStyle _BgStyle;
        public GUIStyle BgStyle
        {
            get
            {
                if (_BgStyle == default)
                {
                    _BgStyle = new GUIStyle("button");
                    _BgStyle.margin = new RectOffset(0, 0, 0, 0);
                    _BgStyle.padding = new RectOffset(0, 0, 0, 0);
                }
                return _BgStyle;
            }
        }
        #endregion


        public FlowNodeSurface(FlowNode t, float curScale = -1) : base(t)
        {
            if (curScale == -1) { curScale = maxScale; }
            else { this.curScale = curScale; }

            FieldInfo[] fieldInfos = t.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            foreach (var info in fieldInfos)
            {
                if (info.IsDefined(typeof(InputGroupAttribute)))
                {
                    Type portGroupType = typeof(PortGroupSurface<>).MakeGenericType(info.FieldType.GenericTypeArguments[0]);
                    var portGroupSurface = Activator.CreateInstance(portGroupType, this, info.GetValue(t), curScale, true, info.Name);
                    inPortSurface.Add(portGroupSurface as NodeComponentSurface);
                }
                else if (info.IsDefined(typeof(OutputGroupAttribute)))
                {
                    var portGroupType = typeof(PortGroupSurface<>).MakeGenericType(info.FieldType.GenericTypeArguments[0]);
                    var portGroupSurface = Activator.CreateInstance(portGroupType, this, info.GetValue(t), curScale, false, info.Name);
                    outPortSurface.Add(portGroupSurface as NodeComponentSurface);
                }
            }

            ConstrucSurfaceList(t.inputPorts, inPortSurface);
            ConstrucSurfaceList(t.outputPorts, outPortSurface);
        }

        public FlowNodeSurface(FlowNode t, Vector2 position, float curScale, ValueWrap contextVar = default) : this(t, curScale) 
        {
            this.position = position;
            if (contextVar != default)
            {
                contextKeyField = t.GetType().GetField("key", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                if (contextKeyField != null)
                {
                    this.contextVar = contextVar;
                    contextKeyField.SetValue(t, contextVar.name);
                    contextNodeName = ContextNodeName();
                }
            }
        }

        public override void ChildrenEventHandler()
        {
            base.ChildrenEventHandler();
            foreach (var portSurf in inPortSurface)
            {
                portSurf.OnEvent();
            }

            foreach (var portSurf in outPortSurface)
            {
                portSurf.OnEvent();
            }
        }

        #region 显示刷新

        // 传入Graph的绝对Rect
        public override void OnDraw(Rect graphRect)
        {
            // 画 Node 本身的元素
            if (isActive)
            {
                tmpColor = GUI.color;
                GUI.color = hightLightColor;
                GUI.Box(new Rect(position + hightLightRelativeRect.position, size + hightLightRelativeRect.size), "", highlightStyle);
                GUI.color = tmpColor;
            }
            relativeRect = new Rect(position, size);
            GUI.Box(relativeRect, "", BgStyle);
            GUI.Box(new Rect(position, headerSize), showName, headerStyle);

            // 画port
            foreach (var portSurface in inPortSurface)
            {
                portSurface.OnDraw(relativeRect);
            }

            foreach (var portSurface in outPortSurface)
            {
                portSurface.OnDraw(relativeRect);
            }

            if (isHovering)
            {
                EditorGUIUtility.AddCursorRect(relativeRect, MouseCursor.Pan);
            }
        }

        public override void RefreshScale(float newScale, Vector2 mousePosition)
        {
            // 由于Node的Style和Size依赖于子元素，因此先对子元素进行ReScale
            foreach (var portSurf in inPortSurface) { portSurf.RefreshScale(newScale, mousePosition); }
            foreach (var portSurf in outPortSurface){ portSurf.RefreshScale(newScale, mousePosition); }

            position = (mousePosition + (position / curScale * maxScale - mousePosition)) * newScale / maxScale;
            base.RefreshScale(newScale, mousePosition);
            RefreshAppearance();
        }

        public override void RefreshStyle()
        {
            foreach (var portSurf in inPortSurface) { portSurf.RefreshStyle(); }
            foreach (var portSurf in outPortSurface) { portSurf.RefreshStyle(); }

            headerStyle = new GUIStyle(basicHeaderStyle);
            headerStyle.fontSize = (int)Mathf.Floor(basicHeaderStyle.fontSize * curScale / maxScale);
            if (headerStyle.fontSize <= 0) { headerStyle.fontSize = 1; }
            headerStyle.padding = basicHeaderStyle.padding.Multiply(curScale / maxScale);
            headerStyle.margin = basicHeaderStyle.margin.Multiply(curScale / maxScale);
        }

        public override void RefreshSize()
        {
            foreach (var portSurf in inPortSurface) { portSurf.RefreshSize(); }
            foreach (var portSurf in outPortSurface) { portSurf.RefreshSize(); }

            realHeaderSize = headerStyle.CalFullSize(showName);
            inRealBodySize = CalculatePortsSize(inPortSurface);
            outRealBodySize = CalculatePortsSize(outPortSurface);

            hightLightRelativeRect = new Rect(basicHighLightRelativeRect);
            hightLightRelativeRect = basicHighLightRelativeRect.Multiply(curScale / maxScale);

            size = CalculateNodeSize();
        }

        public void RefreshAppearance()
        {
            RefreshStyle();
            RefreshSize();
            SetPortsPosition();
        }
        #endregion

        #region Size Position 计算

        public Vector2 CalculatePortsSize(List<NodeComponentSurface> portSurfaces)
        {
            Vector2 size = Vector2.zero;
            foreach (var portSurface in portSurfaces)
            {
                if (portSurface.size.x > size.x) { size.x = portSurface.size.x; }
                size.y += portSurface.size.y;
            }
            return size;
        }

        public Vector2 CalculateNodeSize()
        {
            Vector2 size = default;
            size.x = (inRealBodySize.x + outRealBodySize.x) > realHeaderSize.x ? (inRealBodySize.x + outRealBodySize.x) : realHeaderSize.x;
            size.y = realHeaderSize.y + ((inRealBodySize.y > outRealBodySize.y) ? inRealBodySize.y : outRealBodySize.y);
            if (size.x < minSize.x * curScale / maxScale) { size.x = minSize.x * curScale / maxScale; }
            if (size.y < minSize.y * curScale / maxScale) { size.y = minSize.y * curScale / maxScale; }
            return size;
        }

        // 计算每个Port的相对Node的position，在初始化以及Node、Port name changed、scale chg 的时候被调用
        public void SetPortsPosition()
        {
            Vector2 pos = new Vector2(0, realHeaderSize.y);
            foreach (var portSurface in inPortSurface)
            {
                portSurface.SetPosition(new Vector2(pos.x, pos.y));
                pos.y += portSurface.size.y;
            }
            pos.y = realHeaderSize.y;
            foreach (var portSurface in outPortSurface)  
            {
                pos.x = size.x - portSurface.size.x;
                portSurface.SetPosition(new Vector2(pos.x, pos.y));
                pos.y += portSurface.size.y;
            }
        }

        #endregion

        #region Port相关

        // 将隶属于Group的port全权交给Group管理
        private void ConstrucSurfaceList(Dictionary<string, Port> portDic, List<NodeComponentSurface> portSurf)
        {
            bool isContaining;
            foreach (var i in portDic)
            {
                isContaining = false;
                foreach (var surface in portSurf)
                {
                    if (surface.Contains(i.Value)) { isContaining = true; break; }
                }
                if (!isContaining)
                {
                    if (i.Value.GetType().IsSubclassOf(typeof(FlowPort)))
                    { portSurf.Insert(0, new PortSurface(this, i.Value, curScale)); }
                    else { portSurf.Add(new PortSurface(this, i.Value, curScale)); }
                }
            }
        }

        public PortSurface GetActivePortSurf()
        {
            return GetActivePortSurf(inPortSurface) ?? GetActivePortSurf(outPortSurface);
        }

        public PortSurface GetActivePortSurf(List<NodeComponentSurface> portSurfaces)
        {
            PortSurface portSurface;
            foreach (var portSurf in portSurfaces)
            {
                portSurface = portSurf.GetActiveMember();
                if (portSurface != null)
                {
                    return portSurface;
                }
            }
            return null;
        }

        // TODO 改掉
        public PortSurface GetPortSurface(Port port)
        {
            foreach (var portSurf in inPortSurface)
            {
                if(portSurf.GetType() == typeof(PortSurface))
                if ((portSurf as PortSurface).target == port) { return portSurf as PortSurface; }
            }
            foreach (var portSurf in outPortSurface)
            {
                if (portSurf.GetType() == typeof(PortSurface))
                    if ((portSurf as PortSurface).target == port) { return portSurf as PortSurface; }
            }
            return null;
        }

        #endregion

        #region 序列化
        public override void OnDeserialize_t(ISerializeEntity entity)
        {
            base.OnDeserialize_t(entity);
            RefreshScale(curScale, position);
            RefreshAppearance();
        }

        #endregion

        #region 生命周期

        public override void OnCreate()
        {
            base.OnCreate();
            RefreshAppearance();
            EventCenter.Instance.Register<FlowNode>((int)EventCenterNumber.OnNodeValueChg, OnNodeValueChange);
            if (contextKeyField != null && contextVar != null)
            {
                EventCenter.Instance.Register<ValueWrap>((int)EventCenterNumber.OnContextNameChg, OnContextNameChange);
            }
        }

        public override void OnDestroy()
        {
            base.OnDestroy();
            EventCenter.Instance.Unregister<FlowNode>((int)EventCenterNumber.OnNodeValueChg, OnNodeValueChange);
            if (contextKeyField != null && contextVar != null)
            {
                EventCenter.Instance.Unregister<ValueWrap>((int)EventCenterNumber.OnContextNameChg, OnContextNameChange);
            }
        }

        #endregion

        private void OnNodeValueChange(FlowNode t)
        {
            if (t == target)
            {
                RefreshAppearance();
            }
        }

        private void OnContextNameChange(ValueWrap contextVar)
        {
            if (contextKeyField != null && this.contextVar != null && this.contextVar == contextVar)
            {
                contextNodeName = ContextNodeName(); ;
                RefreshAppearance();
            }
        }

        private string ContextNodeName()
        {
            Type genericType = VariableHelper.Convert(contextVar.type);
            if (target.GetType() == typeof(GetObject<>).MakeGenericType(genericType))
            { return "§ Get " + contextVar.name + " [" + genericType.FriedlyName() + "]"; }
            else if(target.GetType() == typeof(SetObject<>).MakeGenericType(genericType))
            { return "§ Set " + contextVar.name + " [" + genericType.FriedlyName() + "]"; }
            return default;
        }
    }
}